package com.zlyx.easy.core.http;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import com.zlyx.easy.core.map.Maps;
import com.zlyx.easy.core.utils.JsonUtils;
import com.zlyx.easy.core.utils.ObjectUtils;

/**
 * <p>
 * http客户端工具响应
 * </p>
 *
 * @author 赵光
 * @since 2019年4月8日
 */
@Component
public class HttpUtils {

	private static Logger logger = LoggerFactory.getLogger(HttpClient.class);

	private static RestTemplate restTemplate;

	public HttpUtils(@Autowired(required = false) RestTemplate restTemplate) {
		if (restTemplate == null) {
			restTemplate = new RestTemplate();
		}
		HttpUtils.restTemplate = restTemplate;
	}

	public static RestTemplate restTemplate() {
		if (restTemplate == null) {
			restTemplate = new RestTemplate();
		}
		return restTemplate;
	}

	/**
	 * getHttpEntity
	 *
	 * @param params
	 * @return
	 */
	public static <T> HttpEntity<T> getHttpEntity(T params) {
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
		return new HttpEntity<T>(params, headers);
	}

	/**
	 * getHttpEntity
	 *
	 * @param params
	 * @param mediaType
	 * @return
	 */
	public static <T> HttpEntity<T> getHttpEntity(T params, MediaType mediaType) {
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(mediaType);
		return new HttpEntity<T>(params, headers);
	}

	/**
	 * 客户端
	 */
	public static class HttpClient {

		private String id;

		private String url;

		private Object[] uriVariables;

		private HttpHeaders headers;

		private Map<String, Object> dataMap;

		public HttpClient(String url, Object[] uriVariables) throws Exception {
			this.id = UUID.randomUUID().toString();
			this.url = url;
			this.uriVariables = uriVariables;
			this.dataMap = new TreeMap<>();
		}

		/**
		 * @param url
		 * @param uriVariables
		 */
		public HttpClient(String url, Map<String, Object> uriVariables) {
			this.id = UUID.randomUUID().toString();
			this.url = url;
			this.uriVariables = new Object[0];
			this.dataMap = new TreeMap<>(uriVariables);
		}

		/**
		 * uriVariables
		 *
		 * @param uriVariables
		 * @return
		 * @throws Exception
		 */
		public HttpClient uriVariables(Object... uriVariables) throws Exception {
			this.uriVariables = uriVariables;
			return this;
		}

		/**
		 * 请求头信息
		 *
		 * @param mediaType
		 * @return
		 */
		public HttpClient headers(MediaType mediaType) {
			HttpHeaders headers = new HttpHeaders();
			headers.setContentType(mediaType);
			this.headers = headers;
			return this;
		}

		/**
		 * 请求头信息
		 *
		 * @param headers
		 * @return
		 */
		public HttpClient headers(HttpHeaders headers) {
			this.headers = headers;
			return this;
		}

		/**
		 * 请求参数
		 *
		 * @param key
		 * @param value
		 * @return
		 * @throws Exception
		 */
		public HttpClient param(String json) throws Exception {
			params(JsonUtils.toMap(json));
			return this;
		}

		/**
		 * 请求参数
		 *
		 * @param key
		 * @param value
		 * @return
		 * @throws Exception
		 */
		public HttpClient param(String key, Object value) {
			dataMap.put(key, value);
			return this;
		}

		/**
		 * 请求参数
		 *
		 * @param params
		 * @return
		 * @throws Exception
		 */
		public HttpClient params(Map<String, Object> params) {
			if (params != null) {
				dataMap.putAll(params);
			}
			return this;
		}

		/**
		 * 请求参数
		 *
		 * @param params
		 * @return
		 * @throws Exception
		 */
		public HttpClient params(MultiValueMap<String, Object> params) {
			if (params != null) {
				params(params.toSingleValueMap());
			}
			return this;
		}

		/**
		 * 获取请求参数
		 * 
		 * @return
		 * @throws Exception
		 */
		private Object getParams() throws Exception {
			if (headers != null && headers.getContentType().isCompatibleWith(MediaType.APPLICATION_JSON_UTF8)) {
				return JsonUtils.toJson(dataMap);
			}
			return Maps.toMultiMap(dataMap);
		}

		/**
		 * 获取请求参数
		 * 
		 * @return
		 */
		public String getUrl(HttpMethod httpMethod) {
			if (url.contains("?") || dataMap.isEmpty() || HttpMethod.POST == httpMethod) {
				return url;
			}
			StringBuilder stream = new StringBuilder();
			Object value = "";
			for (Iterator<Entry<String, Object>> entrys = dataMap.entrySet().iterator(); entrys.hasNext();) {
				Entry<String, Object> entry = entrys.next();
				if (entry.getValue() != null && ObjectUtils.isNotEmpty(entry.getValue())) {
					value = entry.getValue();
				}
				stream.append(String.format("&%s=%s", entry.getKey(), value));
				entrys.remove();
			}
			return url + "?" + stream.toString().substring(1);
		}

		/**
		 * 发送请求
		 *
		 * @param httpMethod 请求类型
		 * @return
		 * @throws Exception
		 */
		public HttpResponse execute(HttpMethod httpMethod) throws Exception {
			return execute(httpMethod, HttpResponse.class);
		}

		/**
		 * 发送请求
		 *
		 * @param <T>
		 * @param httpMethod 请求类型
		 * @param cls        返回类型
		 * @return
		 * @throws Exception
		 */
		public <T> T execute(HttpMethod httpMethod, Class<T> cls) throws Exception {
			logger.info("【{}】【{}】 请求地址 ：{}", id, httpMethod, url);
			logger.info("【{}】【{}】 请求参数 ：{}", id, httpMethod, dataMap);
			ResponseEntity<T> response = restTemplate().exchange(getUrl(httpMethod), httpMethod,
					new HttpEntity<>(getParams(), headers), cls, uriVariables);
			logger.info("【{}】【{}】 请求结果 ：{}", id, httpMethod, response.getBody());
			return response.getBody();
		}

		/**
		 * 发送请求
		 *
		 * @param <T>
		 * @param httpMethod    请求类型
		 * @param typeReference 返回类型
		 * @return
		 * @throws Exception
		 */
		public <T> T execute(HttpMethod httpMethod, ParameterizedTypeReference<T> typeReference) throws Exception {
			logger.info("【{}】【{}】 请求地址 ：{}", id, httpMethod, url);
			logger.info("【{}】【{}】 请求参数 ：{}", id, httpMethod, dataMap);
			ResponseEntity<T> response = restTemplate().exchange(getUrl(httpMethod), httpMethod,
					new HttpEntity<>(getParams(), headers), typeReference, uriVariables);
			logger.info("【{}】【{}】 请求结果 ：{}", id, httpMethod, response.getBody());
			return response.getBody();
		}

		/**
		 * 发送请求
		 *
		 * @param <T>
		 * @param httpMethod 请求类型
		 * @return
		 * @throws Exception
		 */
		public <K, V> Map<K, V> executeAndReturnMap(HttpMethod httpMethod) throws Exception {
			return execute(httpMethod, new ParameterizedTypeReference<Map<K, V>>() {
			});
		}

		/**
		 * 发送请求
		 *
		 * @param <T>
		 * @param httpMethod 请求类型
		 * @return
		 * @throws Exception
		 */
		public <T> List<T> executeAndReturnList(HttpMethod httpMethod) throws Exception {
			return execute(httpMethod, new ParameterizedTypeReference<List<T>>() {
			});
		}

		/**
		 * post请求
		 *
		 * @param cls 返回类型
		 * @return
		 * @throws Exception
		 */
		public <T> T post(Class<T> cls) throws Exception {
			return execute(HttpMethod.POST, cls);
		}

		/**
		 * post请求
		 *
		 * @return
		 * @throws Exception
		 */
		public HttpResponse post() throws Exception {
			return post(HttpResponse.class);
		}

		/**
		 * post请求
		 *
		 * @return
		 * @throws Exception
		 */
		public String postString() throws Exception {
			return execute(HttpMethod.POST, String.class);
		}

		/**
		 * post(参数需要转JSON发送)
		 *
		 * @param cls 返回类型
		 * @return
		 * @throws Exception
		 */
		public <T> T postForJson(Class<T> cls) throws Exception {
			headers(MediaType.APPLICATION_JSON_UTF8);
			return post(cls);
		}

		/**
		 * post(参数需要转JSON发送)
		 *
		 * @return
		 * @throws Exception
		 */
		public HttpResponse postForJson() throws Exception {
			return postForJson(HttpResponse.class);
		}

		/**
		 * get请求
		 *
		 * @param cls 返回类型
		 * @return
		 * @throws Exception
		 */
		public <T> T get(Class<T> cls) throws Exception {
			return execute(HttpMethod.GET, cls);
		}

		/**
		 * get请求
		 *
		 * @return
		 * @throws Exception
		 */
		public String getString() throws Exception {
			return execute(HttpMethod.GET, String.class);
		}

		/**
		 * get请求
		 *
		 * @return
		 * @throws Exception
		 */
		public HttpResponse get() throws Exception {
			return get(HttpResponse.class);
		}

		/**
		 * put请求
		 *
		 * @param <T>
		 * @param cls 返回类型
		 * @return
		 * @throws Exception
		 */
		public <T> T put(Class<T> cls) throws Exception {
			return execute(HttpMethod.PUT, cls);
		}

		/**
		 * put请求
		 *
		 * @return
		 * @throws Exception
		 */
		public HttpResponse put() throws Exception {
			return put(HttpResponse.class);
		}

		/**
		 * delete请求
		 *
		 * @param <T>
		 * @param cls 返回类型
		 * @return
		 * @throws Exception
		 */
		public <T> T delete(Class<T> cls) throws Exception {
			return execute(HttpMethod.DELETE, cls);
		}

		/**
		 * delete请求
		 *
		 * @return
		 * @throws Exception
		 */
		public HttpResponse delete() throws Exception {
			return delete(HttpResponse.class);
		}

		/**
		 * 实例化客户端
		 *
		 * @param url          请求地址
		 * @param uriVariables 地址参数
		 * @return
		 * @throws Exception
		 */
		public static HttpClient url(String url, Object... uriVariables) throws Exception {
			return new HttpClient(url, uriVariables);
		}

		/**
		 * 实例化客户端
		 *
		 * @param url          请求地址
		 * @param uriVariables 地址参数
		 * @return
		 * @throws Exception
		 */
		public static HttpClient url(String url, Map<String, Object> uriVariables) throws Exception {
			return new HttpClient(url, uriVariables);
		}

	}

}
